#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Rectangle.h"

#include "Class.h"
#include "Error.h"
#include "Math.h"
#include "String.h"
#include "ET_stdio.h"

const Rectangle gRect0(0,0,0,0);

SimpleMetaImpl(Rectangle)
{
	len= len;
	if (! isptr)
		sprintf(buf, "%s", ((Rectangle*) addr)->AsString());
}

Rectangle::Rectangle() : origin(0,0), extent(0,0)
{
}

Rectangle::Rectangle(int x, int y, int w, int h) : origin(x,y), extent(w,h)
{
}

Rectangle::Rectangle(const Point &o, const Point &e) : origin(o), extent(e)
{
}

Rectangle::Rectangle(const Point &e) : origin(0,0), extent(e)
{
}

Rectangle::Rectangle(int w, int h) : origin(0,0), extent(w,h)
{
}

Rectangle::Rectangle(const Rectangle &r) : origin(r.origin), extent(r.extent)
{
}

Rectangle NormRect(const Point &p1, const Point &p2)
{
	Rectangle r;

	r.origin.x= Math::Min(p1.x, p2.x);
	r.origin.y= Math::Min(p1.y, p2.y);
	r.extent.x= Math::Max(p1.x, p2.x)+1-r.origin.x;
	r.extent.y= Math::Max(p1.y, p2.y)+1-r.origin.y;
	return r;
}

bool Rectangle::Intersects(const Rectangle &r) const
{
	return origin.x < r.origin.x + r.extent.x
		&& r.origin.x < origin.x + extent.x
		&& origin.y < r.origin.y + r.extent.y
		&& r.origin.y < origin.y + extent.y;
}

bool Rectangle::ContainsRect(const Rectangle &r) const
{
	Point c1(origin + extent), c2(r.origin + r.extent);

	return origin.x <= r.origin.x && c1.x >= c2.x
		&& origin.y <= r.origin.y && c1.y >= c2.y;
}

bool Rectangle::OvalContainsRect(const Rectangle &r) const
{
	int rad= Math::Min(extent.x, extent.y)/2;
	Point c(Center());

	if (Length(r.NW()-c) >= rad)
		return FALSE;
	if (Length(r.NE()-c) >= rad)
		return FALSE;
	if (Length(r.SW()-c) >= rad)
		return FALSE;
	if (Length(r.SE()-c) >= rad)
		return FALSE;
	return TRUE;
}

Rectangle Rectangle::Merge(const Rectangle &r)
{
	register int x1, x2, y1, y2;

	if (IsEmpty())
		return *this= r;
	if (r.IsEmpty())
		return *this;
	x1= origin.x + extent.x;
	y1= origin.y + extent.y;
	x2= r.origin.x + r.extent.x;
	y2= r.origin.y + r.extent.y;
	origin.x= Math::Min(origin.x, r.origin.x);
	origin.y= Math::Min(origin.y, r.origin.y);
	extent.x= Math::Max(x1, x2) - origin.x;
	extent.y= Math::Max(y1, y2) - origin.y;
	return *this;
}

Rectangle Union(const Rectangle &r1, const Rectangle &r2)
{
	Rectangle rr(r1);
	return rr.Merge(r2);
}

bool Rectangle::Clip(const Rectangle &r)
{
	register int p1x, p1y, p2x, p2y;

	if (IsEmpty())
		return FALSE;
	if (r.IsEmpty()) {
		*this= gRect0; // g++ 1.37.2.beta bug on 68020 cpu ?
		return FALSE;
	}
	p1x= origin.x + extent.x;
	p1y= origin.y + extent.y;
	p2x= r.origin.x + r.extent.x;
	p2y= r.origin.y + r.extent.y;
	origin.x= Math::Max(origin.x, r.origin.x);
	origin.y= Math::Max(origin.y, r.origin.y);
	extent.x= Math::Min(p1x, p2x) - origin.x;
	extent.y= Math::Min(p1y, p2y) - origin.y;
	return IsNotEmpty();
}

Rectangle Inter(Rectangle r1, const Rectangle &r2)
{
	r1.Clip(r2);
	return r1;
}

Rectangle Rectangle::Intersect(const Rectangle &r)
{
	Clip(r);
	return *this;
}

Rectangle BoundingBox(int npts, Point *pts, Point *np)
{
	Point mi(pts[0]), ma(pts[0]);
	register int i;

	if (np == 0)
		np= pts;

	for (i= 1; i<npts; i++) {
		mi= Min(mi, pts[i]);
		ma= Max(ma, pts[i]);
	}
	Rectangle r(mi, ma-mi+1);

	for (i= 0; i<npts; i++)
		np[i]= pts[i]-mi;
		
	return r;
}

Rectangle Rectangle::WedgeBBox(int s, int d)
{
	Point mi, ma, p;
	register int i;

	if (d >= 270)
		return *this;

	mi= ma= Center();
	p= AngleToPoint(s);
	mi= Min(p, mi);
	ma= Max(p, ma);
	i= 1;
	for (i= 1; i < d-1; i++) {
		switch (s+i) {
		case 0:
			mi.y= origin.y;
			break;
		case 90:
			ma.x= origin.x + extent.x;
			break;
		case 180:
			ma.y= origin.y + extent.y;
			break;
		case 270:
			mi.x= origin.x;
			break;
		default:
			continue;
		}
	}
	p= AngleToPoint(s+d);
	return NormRect(Min(p, mi), Max(p, ma));
}

int Rectangle::Diff(Rectangle *rp, const Rectangle &r1, const Rectangle &r2)
{
	Rectangle in(Inter(r1, r2));
	int n= Difference(rp, r1, in);
	n+= Difference(&rp[n], r2, in);
	return n;
}

int Difference(Rectangle *rp, const Rectangle &r1, const Rectangle &ra)
{
	register int p2x, p2y;
	Rectangle dr[4];
	int n, i;

	Rectangle r2(Inter(r1, ra));
	if (r2.IsNotEmpty()) {
		p2x= r2.origin.x+r2.extent.x;
		p2y= r2.origin.y+r2.extent.y;
		
		dr[0].origin= r1.origin;
		dr[0].extent.x= r1.extent.x;
		dr[0].extent.y= r2.origin.y-r1.origin.y;
		
		dr[1].origin.x= r1.origin.x;
		dr[1].origin.y= r2.origin.y;
		dr[1].extent.x= r2.origin.x-r1.origin.x;
		dr[1].extent.y= r2.extent.y;
		
		dr[2].origin.x= p2x;
		dr[2].origin.y= r2.origin.y;
		dr[2].extent.x= r1.origin.x+r1.extent.x-p2x;
		dr[2].extent.y= r2.extent.y;
		
		dr[3].origin.x= r1.origin.x;
		dr[3].origin.y= p2y;
		dr[3].extent.x= r1.extent.x;
		dr[3].extent.y= r1.origin.y+r1.extent.y-p2y;
	} else
		dr[0]= r1;

	for (n= i= 0; i<4; i++)
		if (dr[i].IsNotEmpty())
			rp[n++]= dr[i];
	return n;
}

Rectangle Rectangle::Inset(const Point &p) const
{
	Rectangle r;

	r.origin.x= origin.x + p.x;
	r.origin.y= origin.y + p.y;
	r.extent.x= extent.x - 2 * p.x;
	r.extent.y= extent.y - 2 * p.y;
	return r;
}

Rectangle Rectangle::Expand(const Point &p) const
{
	Rectangle r;

	r.origin.x= origin.x - p.x;
	r.origin.y= origin.y - p.y;
	r.extent.x= extent.x + 2 * p.x;
	r.extent.y= extent.y + 2 * p.y;
	return r;
}
		
Point Rectangle::Constrain(const Point &p)
{
	return Max(Min(p, origin+extent), origin);
}
		
Point ConstrainMoveRect(const Rectangle &r1, const Rectangle &r2, Point delta)
{
	Point p= r1.origin + r1.extent - r2.extent;
	p= Min(p, r2.origin+delta);
	return Max(r1.origin, p)-r2.origin;
}

Point Rectangle::AmountToTranslateWithin(const Rectangle &r)
{
	Point delta(0), rUL= NW(), rLR= SE(), vrUL= r.NW(), vrLR= r.SE();

	for (int v= 0; v <= 1; v++) {
		if (rUL[v] < vrUL[v] && rLR[v] > vrLR[v])
			continue;
		if (rLR[v] - vrUL[v] < extent[v])
			delta[v]= vrUL[v] - rUL[v];
		if (vrLR[v] - rUL[v] < extent[v])
			delta[v]= vrLR[v] - rLR[v];
	}
	return delta;
}

OStream& operator<< (OStream &s, const Rectangle &r)
{
	return s << r.origin << r.extent;
}

IStream& operator>> (IStream &s, Rectangle &r)
{
	return s >> r.origin >> r.extent;
}

int Rectangle::PointToAngle(const Point &p) const
{
	Point pp(p-Center());
	return (int) Math::Atan2(pp.y*extent.x, pp.x*extent.y);
}

Point Rectangle::OvalAngleToPoint(int ang) const
{
	return Center() + PolarToPoint(ang, extent.x/2, extent.y/2);
}

Point Rectangle::AngleToPoint(int a) const
{
	double si, co;
	register int x= 0, y= 0;

	Math::Sincos(a, &si, &co);

	if (si != 0.0) {
		x= (int) ((1.0 + co/Math::Abs(si))/2.0 * extent.x);
		x= Math::Range(0, extent.x, x);
	} else if (co >= 0.0)
		x= extent.x;
	if (co != 0.0) {
		y= (int) ((1.0 + si/Math::Abs(co))/2.0 * extent.y);
		y= Math::Range(0, extent.y, y);
	} else if (si >= 0.0)
		y= extent.y;
	return origin + Point(x, y);
}

// Corners:
//         7|0|1
//        -------
//         6| |2
//        -------
//         5|4|3

int Rectangle::PointToCorner(const Point &p) const
{
	return ((PointToAngle(p)+22)/45) % 8;
}

Point Rectangle::CornerToPoint(int n) const
{
	return AngleToPoint((n % 8)*45);
}

const char *Rectangle::AsString() const
{
	return form("x: %d y: %d w: %d h: %d", origin.x, origin.y, extent.x, extent.y);
}

